﻿/********************************************************************
	created:	2016/03/12
	created:	12:3:2016   15:01
	filename: 	E:\cocos_learn\Documents\Cocos\CocosProjects\GnsClientFrame\Common\MemBlock\MemBlock2.h
	file path:	E:\cocos_learn\Documents\Cocos\CocosProjects\GnsClientFrame\Common\MemBlock
	file base:	MemBlock
	file ext:	h
	author:		hgj
	
	purpose:	
*********************************************************************/

#ifndef MEM_BLOCK2_H
#define MEM_BLOCK2_H

#define WIN32_LEAN_AND_MEAN

#include <iostream>  // std::cout
#include <utility>
#include <functional>
#include <atomic>
#include <chrono>    // std::chrono::seconds
#include <thread>    // std::thread, std::this_thread::sleep_for , std::thread
#include <mutex>    // std::mutex, std::lock, std::unique_lock
#include <ctime>
#include <ratio>       // std::ratio
#include <condition_variable>    // std::condition_variable

#include <algorithm>

#include <string>
#include <vector>
#include <map>
#include <deque>
#include <string>
#include <typeinfo>  

#include "stdarg.h"    // 用省略号指定参数表

class  CMemBlock2
{
public:
	enum {	MSG_BLOCK_SIZE = 2048, // 默认的数据块长度
		 };

	enum{	TEXT_MARK_LEN = 64,	// 文字标识的长度，目的是在查询未释放的内存是知道是程序的哪一句申请的
		};
	enum{
		STATE_IN_POOL = 0,	// 在池内
		STATE_OUT_POOL		// 在池外
	};
public:
	
	/* ----------------------------------------------------------
	传入参数：
		iDataSize		分配给 pData 的空间，即数据块的 BUFSIZ
		pszTextMark		文字标识，目的是在查询未释放的内存是知道是程序的哪一句申请的
		                这个参数对效率影响非常大，如下：
						    若为 nullptr ：release、多线程、ThreadGuardEx 下每毫秒 7500 次
							若为字串  ：release、多线程、ThreadGuardEx 下每毫秒 1118 次。
						建议：在内核代码里，因为写代码者比较有把握，用 nullptr ；
						      在应用层里，因为人比较杂，为安全起见，用函数名作为字串；
							  或者先用字串，待程序经过时间验证很稳定后，再改成 nullptr
		iTextLen		pszTextMark 的长度
	-----------------------------------------------------------*/
	CMemBlock2(char* pszTextMark, int iDataSize);

	CMemBlock2(void);
	~CMemBlock2(void);

	static int min2(int a, int b);

	// 分配给消息块的整个空间长度
	int size()	{	return m_iDataSize;	}

	// 有效内容的长度，即 wr_ptr() 与 rd_ptr() 的差距值 
	int length() {	return (m_iWdPtr - m_iRdPtr);  }

	// 还没有写内容的空间，即 wr_ptr 及其之后的空间长度
	int space()	{	return (m_iDataSize - m_iWdPtr);	}	

	// 指向消息块的头
	char* base(){	return m_pData;	}	

	/* --------------------------------------------------------------------------
	函数说明：移动读指针
	传入参数：
		iMove	移动的距离
	传出参数：
	返回值：
		成功则返回 0，失败返回错误码如下：
			10	超出写指针的长度了
			20	iMove 的负值过大
	--------------------------------------------------------------------------*/
	int rd_ptr(int iMove);

	// 获得读指针
	char* rd_ptr();

	// wd_ptr 移到了起点，但 m_pData 中的数据内容并没清零（可节约CPU的清零时间）
	// 比 clear 函数快了将近一倍，说明 memset 是很耗时的
	void reset ()
	{	
		m_iRdPtr = 0;
		m_iWdPtr = 0; 
		m_pData[m_iDataSize-1] = 0; // 保险起见，仅仅最后一个元素清0	

		clear_text_mark();
	}

	// wd_ptr 移到了起点，且 m_pData 中的数据内容清零
	// 比 reset 函数慢了将近一倍，说明 memset 是很耗时的
	void clear()
	{	
		memset(m_pData, 0, m_iDataSize); 
		m_iRdPtr = 0; 
		m_iWdPtr = 0;	

		clear_text_mark();
	}

	// 向消息块中考入内容
	int copy(CMemBlock2* pMemBlock);

	/* ----------------------------------------------------------
	函数说明：向消息块中考入内容
	传入参数：
		pData		要考入的内容
		iDataLen	要考入内容的长度
	返回值：
		成功则返回 0，否则返回错误码如下：
			1  要考入的内容为空
			2  要考入内容的长度为0
			3  要考入内容的长度超长
	-----------------------------------------------------------*/
	int copy(const char* pData, int iDataLen);

	//// 向消息块中考入内容
	//int copy(CMemBlock2* pMsgBlock);
		
	/* ----------------------------------------------------------
	函数说明：移动写指针
	传入参数：
		iLen	移动的长度
	返回值：
		成功则返回 0，否则返回错误码如下：
			3  移动的长度超长
	-----------------------------------------------------------*/
	int write(int iLen,bool bEraseLeft=false);

	// 将残余的内容移到头
	int remains_move_to_head();

	// 获得文字标识，目的是在查询未释放的内存是知道是程序的哪一句申请的
	void get_text_mark(char* pszTextMark, int iMaxLen);

	// 设置文字标识，目的是在查询未释放的内存是知道是程序的哪一句申请的
	void set_text_mark(char* pszTextMark, int iTextLen);

	// 清空文字标识，目的是在查询未释放的内存是知道是程序的哪一句申请的
	void clear_text_mark();

private:

	bool create();

	// 释放所有的资源
	bool release();

public:
	/* --------------------------------------------------------------------------
	函数说明：安全的字符串拷贝
	传入参数：
		iDstSize		目标字串的最大尺寸，即 sizeof 的值
		szSrc			源字串
		iSrcLen		源字串的长度，即 strlen 的值
	传出参数：
		szDst			目标字串
	返回值：
		返回拷贝成功的长度。
	-------------------------------------------------------------------------- */
	static int snprintf_safe(char* szDst, int iDstSize, const char* szSrc, int iSrcLen);

	/* --------------------------------------------------------------------------
	函数说明：安全的字符串拷贝
	传入参数：
		iDstSize		目标内存块的最大尺寸，即 sizeof 的值
		szSrc			源内存块
		iSrcSize		源内存块的长度，即 sizeof 的值
	传出参数：
		szDst			目标内存块
	返回值：
		返回拷贝成功的长度。
	-------------------------------------------------------------------------- */
	static int memcpy_safe(char* szDst, int iDstSize, const char* szSrc, int iSrcSize);

public:
	bool	m_bInPool = true;		// TRUE 表示在池内，FALSE 表示在池外

	bool	bTimeOutPool = false;		// 有出池时间，若为零值表示在池内
	std::chrono::high_resolution_clock::time_point  timeOutPool;			// 出池时间，若为零值表示在池内
																															// 需要赋初值

private:

	int		m_iDataSize = MSG_BLOCK_SIZE;	// 分配给 pData 的空间，即数据块的 BUFSIZ
	int		m_iRdPtr = 0;		// 读串的起点
	int		m_iWdPtr = 0;		// 写串的起点

	char*	m_pData = nullptr;

	char		m_szTextMark[TEXT_MARK_LEN];	// 文字标识，目的是在查询未释放的内存是知道是程序的哪一句申请的。通常是调用的函数名。
	int		m_sizeTextMark = 0;					// 文字标识的长度
};

#endif